![m4sfg logo](img/icon.png)
# m4cfg


A <s>gross</s> very good and useful
[Makefile](https://www.gnu.org/software/make/make.html)-and-[m4](https://www.gnu.org/software/m4/m4.html)-based
system for consistently generating configuration files across many
hosts.

# Wow, Sounds Great! Where do I sign up?

Check out the git repo, ya dingus

    $ git clone https://notabug.org/al/m4cfg

And if you want to see it in action (or want to perv on my private
configuration), check out the `mein-doots` branch:

    $ git checkout -b mein-doots

# <s>What the fuck is this steaming pile of shit and</s> How do I use it?

The basic principle behind this system is that you'll create a
configuration *package* for each program/system you use. Each package
has a *name* and an *output file*. When you run the root `Makefile`,
the output file will be generated. Usually included in a configuration
package is an `install.sh` script. It (usually) does exactly what
you'd expect - in fact since you're the one who's gunna be writing it,
hopefully you know exactly what it does...

## Creating a Configuration Package

Each configuration package has a *name* and an *output file*. So to
generate a new configuration package, all that's needed is to pass
these parameters to the root `Makefile`, using the `addcfg` target:

    $ make NAME='packagename' OUTPUT='package.cfg' addcfg
	mkdir: created directory 'packagename/'
	
Success!

Now if you `cd` into the new package directory, you'll find a few things:

    packagename/
	├── base.m4
	├── hosts.txt
	└── Makefile
	
Usually you won't need to do anything to `hosts.txt` or `Makefile`,
unless you're looking at doing something strange and
unconventional. However, `base.m4` is where you place the
configuration data which is common to all hosts. Since its processed
as an M4 file, you can do all sorts of wacky shit with it. Like
including host-specific configuration from another m4 file...

**packagename/base.m4**
    
	include(HOST`.m4')

Where `HOST` is a predefined macro containing the hostname of the
current system (or the `HOST` variable passed to the root
`Makefile`. More on this later...). So if you're on a system named
`kevin`, this line will look for an m4 macro file called `kevin.m4` in
the package directory at compile-time.

After placing some configuration data in `base.m4`, we return to the
root and compile the configuration:

    $ make
	
Note that this will actually compile *all* of the available
configuration packages, but for our purposes this is fine for
now. This command will cook you a big steaming pile of `packagename`,
and then you'll find a `package.cfg` file sitting in the
`packagename/kevin/` directory. What you do with it from there is
totally up to you. Personally, I add an `install.sh` to each package
directory and run it manually to install the output file to its
relevant location. Something along the lines of this:

**packagename/install.sh**

```bash
#!/bin/sh
TARGET="$HOME/.config/package"
[ -z "$HOST" ] && HOST=$(hostname)
[ ! -d "$TARGET" ] && mkdir -pv "$TARGET"
ln -srvf "$HOST/package.cfg" "$TARGET/"
```
It's probably a good idea to use symbolic links so you don't have to
reinstall the configuration files every time you change them.

## Build Options
Currently, there's a couple of variables that you can pass to the root
`Makefile` to alter its behaviour:

| Variable | Behaviour                                                                 |
|:---------|:--------------------------------------------------------------------------|
| `HOST`   | Overrides the target host. Defaults to the hostname of the current system |
| `NAME`   | Set the target configuration package                                      |
| `OUTPUT` | Set the name of the output file for a particular package                  |

# Building for Many Hosts
The root `Makefile` has an `allhosts` target. It does exactly what you
think, assuming you are a rational person. Note that before using this
target, you need to add the target hostnames to each configuration
package. This can be done using

    $ make NAME='target-package' addme
	
or by manually adding hostnames to the `target-package/hosts.txt`
file. Using the make method, the `HOST` variable can also be altered
to add foreign hosts.

Running `make allhosts` will then cook up many steaming piles of
configuration.

![make allhosts](img/2.gif)

# Removing Configuration Packages
This is done using the `rmcfg` target. You'll also need to pass it a package name:

    $ make NAME='target-package' rmcfg
	
*Only do this if you're absolutely sure. There are no sanity checks!*

It is important that you use this method instead of manually running
`rm -rf` on the package directory; m4cfg also needs to update some
internal files.

Alternatively, if you're in the mood for danger, you can run `make
purge` to **irrevocably remove every single configuration package**

# Creating Package Archives
You can generate package archives in the superior `tar.xz` format
using the `archive` target on the root `Makefile`. If you pass in the
`NAME` variable, it will generate a tarball containing just the
contents of that particular package. Otherwise, an archive with the
contents of the entire configuration tree will be generated.

# Help! There are duplicates in `export.txt` / `hosts.txt`!

    make clean # <- Don't worry. Is safe.
